import {HTML_NODE_TYPE} from "../../constant";

/**
 * 生成静态节点的渲染函数代码，并将渲染函数代码放入 staticRenderFns 数组中
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genStatic(astNode, state) {
    // 将生成的函数代码放入 state 中的 staticRenderFns 数组中, 并生成渲染函数代码
    const staticRenderCode = genElement(astNode, state);
    state.staticRenderFns.push(staticRenderCode);
    const index = state.staticRenderFns.length;
    let code = `_m(${index-1})`;
    return code;
}

/**
 * 根据 v-pre 的与发表及，生成 v-pre 的渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genPre (astNode, state) {
    // 标记该节点的 v-pre 语法指令已被处理
    astNode.processedBitMap.pre = true;
    // 编译状态设置 inPre, 并渲染这部分静态HTML的渲染代码
    const inPreBack = state.inPre;
    state.inPre = true;
    const code = genStatic(astNode, state)
    state.inPre = inPreBack;
    // 返回代码
    return code;
}

/**
 * 根据 for 条件的信息生成渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genFor(astNode, state) {
    // 标记该节点的 v-for 语法指令已处理
    astNode.processedBitMap.for = true;

    const inForBack = state.inFor;
    state.inFor = true;
    // 获取 v-for 指令处理信息
    const conditionMessage = astNode.conditionMessage.for;
    // 进行代码的生成
    /**
     * 这里 function 的作用是包装函数的上下文，
     * 用来帮助 genElement 函数产生的渲染函数通过别名寻找变量
     */
    const keyValueStr = astNode.conditionMessage.key
        ? astNode.conditionMessage.key.exp
        : `index`;
    let code = `_l(`;
    code += `(${conditionMessage.obj}), function(${conditionMessage.alias}, index) {`;
    code += `const vnode = ${genElement(astNode, state)}; `;
    code += `const key = ${keyValueStr}; `;
    code += `_b(vnode, key); `;
    code += `return vnode; ` ;
    code += `} `;
    code += `) `;
    state.inFor = inForBack;
    // 返回代码
    return code;
}

/**
 * 根据 if 条件的信息生成渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genIf(astNode, state) {
    // 获取 if 条件
    const conditions = astNode.conditionMessage.if;
    // 如果节点存在 v-once 指令，使用 genOnce 进行代码生成
    function genTernary (astNode, state) {
        return astNode.conditionBitMap.once
            ? genOnce(astNode, state)
            : genElement(astNode, state);
    }
    // 生成代码
    let code = `${ genIfConditions(conditions, state, genTernary) }`;
    return code;
}

/**
 * 根据 v-if 条件生成相应的渲染函数代码
 * @param {Array<{ exp: String, block: ASTNode }>} conditions
 * @param {CodegenState} state
 * @param {Function} altGen
 * @return {string}
 */
function genIfConditions(conditions, state, altGen) {
    // 生成 if 条件的对应代码
    let code = undefined;
    const condition = conditions.shift();
    // 标记该节点 v-if 指令语法已处理
    condition.block.processedBitMap.if = true;
    // 根据 剩余条件 以及 所取的条件 进行代码生成
    if (conditions.length !== 0) {
        // code = `((${condition.exp}) ? ${(altGen||genElement)(condition.block, state)} : ${genIfConditions(conditions, state, altGen)})`;
        code = `(${condition.exp}) ? ${(altGen||genElement)(condition.block, state)} : ${genIfConditions(conditions, state, altGen)}`;
    }
    else {
        code = `(${condition.exp}) ? ${(altGen||genElement)(condition.block, state)} : null`;
    }
    // 返回代码
    return code;
}

/**
 * 根据 v-show 条件生成相应的渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genShow(astNode, state) {
    // 获取 v-show 条件
    const condition = astNode.conditionMessage.show;
    // 标记该节点 v-show 条件已处理
    condition.block.processedBitMap.show = true;
    // 根据 v-show 条件 生成代码
    let code = `(${condition.exp}) ? ${astNode.conditionBitMap.once ? genOnce(astNode, state) : genElement(astNode, state)} : null`;
    // 返回代码
    return code;
}

/**
 * 根据 v-once 的语法标记，生成 v-once 的渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genOnce(astNode, state) {
    // 标记该节点的 v-once 语法指令已处理
    astNode.processedBitMap.once = true;
    // 生成代码
    const inOnceBack = state.inOnce;
    state.inOnce = true;
    let code = genStatic(astNode, state);
    state.inOnce = inOnceBack;
    // 返回代码
    return code;
}

/**
 * 根据属性对象（map类型）,生成对象代码
 * @param {Object} attrMap
 * @param {boolean} isStatic
 * @return {string}
 */
export function genProps(attrMap, isStatic) {
    let code = "{";
    if (isStatic) {
        Object.keys(attrMap).forEach((key) => {
            code += `${key}: '${attrMap[key]}',`;
        });
    }
    else {
        Object.keys(attrMap).forEach((key) => {
            code += `${key}: ${attrMap[key]} ,`;
        });
    }
    code += "}";
    return code;
}

/**
 * 根据数据是否是动态生成不同的数组代码
 * @param {Array} array
 * @param {boolean} isStatic
 * @return {string}
 */
function genArrayStr (array, isStatic) {
    let arrayCode = "[";
    if (isStatic) {
        for (let i=0; i<array.length; i++) {
            arrayCode += ` '${array[i]}',`;
        }
    }
    else {
        for (let i=0; i<array.length; i++) {
            arrayCode += ` ${array[i]},`;
        }
    }
    arrayCode += "]";
    return arrayCode;
}

/**
 * 根据事件对象生成时间对象的代码
 * @param {Object} eventMap
 * @param {boolean} isStatic
 * @return {string}
 */
export function genHandlers(eventMap, isStatic) {
    let code = "{";
    Object.keys(eventMap).forEach((key) => {
        const array = eventMap[key];
        code += ` ${key}: ${genArrayStr(array, isStatic)} ,`;
    });
    code += "}";
    return code;
}

/**
 * 生成节点需要的数据对象字符串
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genData(astNode, state) {
    let code = "{";

    // 生成静态属性
    code += `attrs: ${genProps(astNode.staticAttrMap, true)},`;
    // 生成静态事件
    code += `on: ${genHandlers(astNode.staticEventMap, true)},`;
    // 如果不在 v-pre 的作用域内，则进行动态属性的生成
    if (!state.inPre) {
        // 生成动态属性
        code += `bindAttrs: ${genProps(astNode.dynamicAttrMap, false)},`;
        // 生成动态事件
        code += `bindOn: ${genHandlers(astNode.dynamicEventMap, false)}`;
    }

    // 生成结束
    code += `}`;

    return code;
}

/**
 * 生成孩子节点渲染函数代码数组
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 */
export function genChildren(astNode, state) {
    // 获取孩子节点
    const children = astNode.children;
    // 组装孩子节点渲染函数数组
    let code = `[`;
    children.forEach((child) => {
        if (!child.generated) {
            code += `${genNode(child, state)} ,`;
        }
    });
    code += `]`;
    // 返回代码
    return code;
}

/**
 * 生成组件内插槽占位节点的渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string}
 */
export function genSlot (astNode, state) {
    // 获取 astNode slot 语法信息
    const conditionMessage = astNode.conditionMessage.slotDeclaration;
    // 获取该插槽占位节点的名称
    const name = conditionMessage.name;
    // 获取绑定的数据信息
    const data = genData(astNode, state);
    // 生成渲染代码
    /**
     * fixme 为什么这里不加上 componentInstance ?
     * fixme 因为本身在生成代码的时候，componentInstance 就已经作为 complie 函数的作用域了
     * fixme 所以理论上这里可以使用 this 能够达到一样的效果
     */
    const code = `_t('${name}', ${data}) `;
    return code;
}

/**
 * 为组件声明的插槽生成渲染函数代码
 * @param {ASTNode} scopeSlot slot插槽节点
 * @param {CodegenState} state
 * @return {string}
 */
export function genScopeSlot(scopeSlot, state) {
    // 标记该节点的 v-slot 语法指令已处理
    scopeSlot.processedBitMap.slotImplement = true;
    // 获取 v-slot 指令处理信息
    const conditionMessage = scopeSlot.conditionMessage.slotImplement;
    // 进行代码的生成
    const slotName = conditionMessage.name;
    const slotScopeAlias = conditionMessage.scope;
    // 生成插槽实现节点的渲染函数代码
    const code = `with(this){ return ${genElement(scopeSlot, state)}; }`;
    return code;
}

/**
 * 代替 genChildren, 为组件节点生成插槽节点代码
 * @param scopeSlotArray
 * @param state
 * @return {string}
 */
export function genScopeSlots (scopeSlotArray, state) {
    let slots = "";
    // 生成插槽节点的渲染函数
    slots = `[`;
    for (let i=0; i<scopeSlotArray.length; i++) {
        const scopeSlot = scopeSlotArray[i];
        // 获取 v-slot 指令处理信息
        const conditionMessage = scopeSlot.conditionMessage.slotImplement;
        const slotName = conditionMessage.name;
        const slotScopeAlias = conditionMessage.scope;
        // 进行代码的生成
        const renderCode = genScopeSlot(scopeSlot, state);
        // 将生成好的信息放入 slots 数组中
        slots += `{ name: '${slotName}', scopeAlias: '${slotScopeAlias}', renderCode: '${renderCode}' }, `;
    }
    slots += `]`;
    // 返回生成好的渲染信息
    return slots;
}

/**
 * 生成组件节点的渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 */
export function genComponent(astNode, state) {
    // 生成数据对象
    const data = genData(astNode, state);
    // 渲染函数的生成？？
    const children = genScopeSlots(astNode.children, state);
    // 代码生成
    let code = `_c("${astNode.tag}", ${data}, ${children})`;
    // 节点生成完毕
    astNode.generated = true;
    // 返回生成代码
    return code;
}

/**
 * 根据 AST 节点生成渲染代码
 *
 * 因为优先级高的指令应该先运行，所以代码应该先生成
 * 这样做为参数传入，会被先调用，先作用在 VNode 上
 *
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 * @return {string|void|*}
 */
export function genElement (astNode, state) {

    // 处理语法部分
    if (!astNode.processedBitMap.pre
        && astNode.conditionBitMap.pre) {
        return genPre(astNode, state);
    }
    if (!astNode.processedBitMap.for
        && astNode.conditionBitMap.for) {
        return genFor(astNode, state);
    }
    if (!astNode.processedBitMap.if
        && astNode.conditionBitMap.if) {
        return genIf(astNode, state);
    }
    if (!astNode.processedBitMap.show
        && astNode.conditionBitMap.show) {
        return genShow(astNode, state);
    }
    if (!astNode.processedBitMap.once
        && astNode.conditionBitMap.once) {
        return genOnce(astNode, state);
    }

    // 节点生成分支
    // template 节点生成分支
    if (astNode.tag === "template") {
        return genChildren(astNode, state);
    }

    // 组件 节点生成分支
    // 处理组件生成部分
    if (astNode.conditionBitMap.component) {
        return genComponent(astNode, state);
    }

    if (astNode.conditionBitMap.slotDeclaration) {
        return genSlot(astNode, state);
    }

    // 常规 节点生成分支
    if (!astNode.generated) {
        // 标记该节点代码已生成
        astNode.generated = true;
        // 生成数据和渲染控制信息对象字符串
        const data = genData(astNode, state);
        // 生成孩子节点的渲染函数的代码
        const children = genChildren(astNode, state);
        // 渲染基础代码
        let code = `_c("${astNode.tag}", ${data ?  data : 'null'}, ${children ? children : 'null'})`;
        return code;
    }
}

/**
 * 生成注释节点渲染函数代码
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 */
export function genComment (astNode, state) {
    return "_e()";
}

/**
 * 根据文本节点的类型进行代码的生成
 * @param {ASTNode} astNode
 * @param {CodegenState} state
 */
export function genText (astNode, state) {
    let code = undefined;
    if (astNode.isDynamic) {
        code = `_v(_s(${astNode.textContent}))`;
    }
    else {
        code = `_v("${astNode.textContent}")`;
    }
    return code;
}

/**
 * 根据节点的类型进行代码的生成
 * @param astNode
 * @param state
 * @return {string}
 */
export function genNode (astNode, state) {
    if (astNode.type === HTML_NODE_TYPE.TAG) {
        return genElement(astNode, state);
    }
    else if (astNode.type === HTML_NODE_TYPE.COMMENT) {
        return genComment(astNode, state);
    }
    else if (astNode.type === HTML_NODE_TYPE.TEXT) {
        return genText(astNode, state);
    }
}


